package NG::Gen::Task::Task;
use strict;
use warnings;
use utf8;

use Data::Dumper;

use NG::Sys::App;
use NG::Sys::Datetime;
use NG::Gen::Task::Common;
use NG::Sys::Data;

our @ISA = qw(NG::Gen::Task::Common);

sub template {
	my $class = shift;
	my $user = shift;
	my $cb = shift;
	my $param = shift;
	
	my $proto = app()->reference("v2_task_prototypes")->get_by_key($cb->{task});
	my $type = app()->reference("v2_task_types")->get_by_key($proto->{type});
	

	my $row = {
			proto_id => $cb->{task},
			leader_type => $param->{leader_type},
			open => unixtime2timestamp(),
			close =>  ($param->{ttl}  ? unixtime2timestamp(time() + $param->{ttl}) : undef),
		};
	delete $param->{leader_type};
	delete $param->{ttl};
	delete $param->{weight};


	app()->logger->info("template: ".Dumper($param));
	
	my $package = "NG::Gen::Task::".$type->{pack};
	Extend::_soft_require($package);
	
	my $self = bless {
				proto => $proto,
				type => $type,
				param => $param,
				user => $user,
				row => $row,
			}, $package;
	
	return $self;
}
sub available {
	my $class = shift;
	my $proto_id = shift;
	my $user = shift;
	my $leader = shift;
	my $param = shift;
	my $user_param = shift;
	
	my $proto = app()->reference("v2_task_prototypes")->get_by_key($proto_id);
	my $type = app()->reference("v2_task_types")->get_by_key($proto->{type});
	
	$param = [
			(ref $proto->{tune} eq "ARRAY" ? @{$proto->{tune}} : ($proto->{tune}||())),
			(ref $param eq "ARRAY" ? @$param : ($param||()))
			];
	
	if($user_param){
		my $tp = array_to_hash(($type->{param}||[]),name => type => "param");
		while(my($k,$v) = each %$tp){
			if(defined $user_param->{$k}){
				# TODO: сделать проверку на валидность данных
				push @$param,{
					name => $k,
					value => $user_param->{$k},
				};
			}
		}
	}
	app()->logger->info("avail: ".Dumper($param));
	
	my $package = "NG::Gen::Task::".$type->{pack};
	Extend::_soft_require($package);
	
	my $self = bless {
				proto => $proto,
				type => $type,
				param => $param,
				user => $user,
				leader => $leader,
			}, $package;
	
	return $self;
}
sub active {
	my $class = shift;
	my $user = shift;
	my $entry = shift;
	
	my $task = $user->atask->get_by_entry($entry);
	unless($task){
		return undef;
	}
	my $leader = $user->leaders->get_by_entry($task->leader_id);
	unless($leader){
		return undef;
	}
	my $proto = $task->proto;
	my $param = $task->param;
	app()->logger->info("active: ".Dumper([$proto,$param]));
	my $type = app()->reference("v2_task_types")->get_by_key($proto->{type});
	
	my $package = "NG::Gen::Task::".$type->{pack};
	Extend::_soft_require($package);
	
	my $self = bless {
				task => $task,
				proto => $proto,
				type => $type,
				param => $param,
				user => $user,
				leader => $leader,
			}, $package;
	
	return $self;
}
sub assign {
	my $self = shift;
	if(my $error = $self->before_assign()){
		return $error;
	}elsif($self->{leader}->d->current_task){
		return "занят";
	}elsif($self->{proto}{flag}{unique} && @{$self->{user}->active_task($self->{proto}{entry})}){
		return "уже выполняется '".$self->{proto}{title}."'";
	}else{
		my $delay;
		unless($self->{proto}{flag}{autodone}){
			$delay = $self->delay;
			my $mtd = app->{cfg}{min_task_delay};
			if($delay and $delay < $mtd){
				$delay = $mtd;
			}
		}
		my $context = $self->make_context;
		my $to_store = {
					proto_id => $self->{proto}{entry},
					leader_id => $self->{leader}->entry,
					started => unixtime2timestamp(),
					done =>  ($delay  ? unixtime2timestamp(time() + $delay) : undef),
					param => $self->{param},
					context => $context,
				};
		my $entry = $self->{user}->atask->insert($to_store);
		unless($entry){
			return "ошибка базы";
		}
		$self->{leader}->d->update_data({current_task => $entry, current_task_proto => $self->{proto}{entry}});
		$self->{entry} = $entry;
		$self->after_assign();
		if($self->{proto}{flag}{autodone}){
			if(my $current = NG::Gen::Task::Task->active($self->{user},$entry)){
				$current->done();
			}
		}
		return 0;
	}
}
sub cancel {
	my $self = shift;
	if(my $error = $self->before_cancel()){
		return $error;
	}elsif(!$self->{proto}{flag}{cancel}){
		return "нельзя отменить";
	}else{
		$self->{task}->remove;
		$self->{leader}->d->current_task(0);
		$self->after_cancel();
		
		if(my $cb = $self->{proto}{events}{on_cancel}){
			if($cb->{task} && (my $new =
			NG::Gen::Task::Task->available(
					$cb->{task},
					$self->{user},
					$self->{leader},
					$cb->{param},
					$self->param_on_cancel,
			))){
				$new->assign;
			}
		}else{
			return 0;
		}
	}
}
sub choose {
	my $self = shift;
	my $choose = shift;

	unless($self->{proto}{flag}{choose}){
		return "нет выбора";
	}elsif(my $cb = $self->{proto}{events}{"choose".$choose}){
		if($cb->{task} && (my $new =
		NG::Gen::Task::Task->available(
				$cb->{task},
				$self->{user},
				$self->{leader},
				$cb->{param},
				$self->param_on_choose,
		))){
			$self->{task}->remove;
			$self->{leader}->d->current_task(0);
			return $new->assign;
		}else{
			return "плохой выбор";
		}
	}
	return 0;
}
sub done {
	my $self = shift;

	if(my $error = $self->before_done()){
		return $error;
	}else{
		if($self->{proto}{flag}{log}){
			my $row = {
				title => $self->{proto}{title},
				started => $self->{task}->started,
				done => $self->{task}->done,
				stoped => unixtime2timestamp(),
				task => {
					type => $self->{type},
					proto => copy_proto($self->{proto}),
					leader => $self->{leader}->proto,
					param => $self->{task}->param,
					context => $self->{task}->context,
					result => $self->{result},
					add => $self->{add},
					},
			};
			$self->{user}->ltask->insert($row);
		}
		$self->{task}->remove;
		$self->{leader}->d->current_task(0);
		if(my $cb = $self->{proto}{events}{on_done}){
			if($cb->{task} && (my $new =
			NG::Gen::Task::Task->available(
					$cb->{task},
					$self->{user},
					$self->{leader},
					$cb->{param},
					$self->param_on_done,
			))){
				$new->assign;
			}
		}
		$self->after_done;
		return 0;
	}
}
sub refresh_user {
	my $self = shift;
	my $need_leader = shift;
	my $refresh_battle = shift;
	if($self->{user}->online){
		my $p = [$self->{proto}{title}];
		if($need_leader){
			push @$p,$self->{leader}->entry;
		}
		if($refresh_battle){
			push @$p,1;
		}
		$self->{user}->chat_command("mc.game.task.after_done",$p);
	}
	return 0;
}

sub copy_proto {
	my $proto = shift;
	my $data = {
		title => $proto->{title},
		titles => $proto->{titles},
		flag => {
			cancel => $proto->{flag}{cancel},
		},
	};

	if($proto->{flag}{choose} && $proto->{events}){
		my $ref = app()->reference("v2_task_prototypes");
		my @choose;
		for(1 .. 3){
			my $e = $proto->{events}{"choose".$_};
			if($e->{task}){
				my $tp = $ref->get_by_key($e->{task});
				push @choose,{
					choose => $_,
					title => $tp->{title},
				};
			}
		}
		$data->{choose} = \@choose;
	}

	return $data;
}
1;
